/**
 * @copyright Copyright (c) 2025 CSG Digital Grid Technology (Guangdong) Co., Ltd.
 * @author Xie Jinhui
 */
/* 
 * Include necessary header files for application functionality
 * This section imports all required system and custom headers
 */
#include "app_action.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

/* 
 * Include custom module headers for specific functionalities
 * These headers provide logging, execution, and profile management capabilities
 */
#include "log_manage.h"
#include "app_execute.h"
#include "appprofile_manage.h"

/* 
 * Define version information constants for the application
 * These constants provide version tracking and identification
 */
#define VERSION "2.1.1-debug"
#define RELEASE_DATE "2025-03-05"

/* 
 * Define lock file path for preventing multiple instances
 * This file ensures only one instance of the application runs at a time
 */
#define LOCK_FILE "/tmp/appctl_program.lock"

/* 
 * Global variable to store lock file descriptor
 * This variable tracks the file descriptor of the lock file
 */
int lock_fd = NULL;

/* 
 * Array of execution messages for various result codes
 * This array maps error codes to human-readable message strings
 * Messages are categorized by type: error, info, debug, warning, verbose
 */
const char* exec_messages[] = {
    [INFO_NO_ERROR] = "    info:appctl exec success",
    [ERROR_ARGUMENT_MISSING] = "    error:argument missing",
    [ERROR_PROFRAAM_RUNNING] = "    error:appctl is already running",
    [INFO_ARGE_HELP] = "    info:appctl help",
    [ERROR_STRING_SUPER_LONG] = "    error:arge string too long",
    [ERROR_ARGE_ERROR] = "    error:arge error",
    [INFO_VERSION] = "    info:appctl version",
    [ERROR_ARGS_ERR] = "    error:parameter command conflict",
    [ERROR_ARGS_APPNAME_NOT_EXIST_ERR] = "    error:lack of application name paramete",
    [ERROR_ARGS_PACKFILEPATH_NOT_EXIST_ERR] = "    error:lack of apppack file path paramete",
    [ERROR_GET_ABSOLUTE_PATH_ERR] = "    error:check appname or get package path error",
    [ERROR_PACKAGE_NO_EXIST_ERR] = "    error:package file no exist",
    [ERROR_AUTHERNTIFICATION_FAIL] = "    error:install authentication failed",
    [ERROR_FILE_SIGNATURE_ERR] = "    error:install file signature error",
    [ERROR_FILE_NOT_EXIT] = "    error:install file not exist",
    [ERROR_INSTALL_APPMANGE_ERR] = "    error:app install appmanager error",
    [ERROR_SUB_MESSAGE_TOKEN_ERR] = "    error:subtopic message token error",
    [ERROR_EXCUTE_ERR] = "    error:app execute error",
    [ERROR_APP_NOT_EXIT] = "    error:app not exist",
    [ERROR_APP_UNINSTALL_ERR] = "    error:app uninstall error",
    [ERROR_APP_START_ERR] = "    error:app start error",
    [ERROR_APP_STOP_ERR] = "    error:app stop error",
    [ERROR_APP_CONTROL_ERR] = "    error:app control error",
    [ERROR_APP_APP_LIST_ERR] = "    error:app list message error",
    [ERROR_JSON_PARSE_ERR] = "    error:message json parse error",
    [ERROR_JSON_MESSAGE_INFO_ERR] = "    error:message json info error",
    [ERROR_APP_APP_STATUS_ERR] = "    error:app status message error",
    [ERROR_APP_APP_RIGHTS_ERR] = "    error:app rights message error",
    [ERROR_APP_PACK_NAME_ERR] = "    error:app package name is not meet the specifications",
    [ERROR_CREATE_APPINSTALL_TMPDIR_ERR] = "    error:failed to create directory",
    [ERROR_CP_APP_PACK_TO_TARGET_DIR_ERROR] = "    error:copy the installation package to the corresponding directory failed",
    [ERROR_CAT_APP_PACK_ERROR] = "    error:split app.pack to .tgz error",
    [ERROR_PACKAGE_DECOMPRESS_ERR] = "    error:appname or installation package error",
    [ERROR_CHAER_APP_FILE_ERROR] = "    error:check app file AppProfile.json error",
    [ERROR_OPEN_APPPROFILE_CTL_ERROR] = "    error:open AppProfile.json to get info error",
    [ERROR_APPPROFILE_GET_APPNAME_ERROR] = "    error: get app name info err",
    [ERROR_APPNAME_MISMATCHING] = "    error: AppProfile.json-appname mismatching -n appname error",
    [ERROR_MAX] = "    error:unknown error"
};

/* 
 * Retrieve error message string for a given result code
 * This function provides a safe way to access error messages from the array
 */
const char* get_error_message(EXEC_RESULE_CODE code) {
    return exec_messages[code];
}

/* 
 * Create and lock a file to prevent multiple program instances
 * This function implements singleton pattern using file locking
 */
static int lock_file(int *fd) {
    /* 
     * Open lock file with create and read-write permissions
     * The file is created if it doesn't exist with 0666 permissions
     */
    *fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0666);
    
    /* 
     * Check if file opening was successful
     * Return error if file cannot be opened
     */
    if (*fd == -1) {
        perror("Failed to open lock file");
        return -1;
    }

    /* 
     * Attempt to acquire exclusive lock on the file
     * Using non-blocking mode to avoid waiting for lock
     */
    if (flock(*fd, LOCK_EX | LOCK_NB) == -1) {
        /* 
         * Handle case where lock is already held by another process
         * This indicates another instance is already running
         */
        if (errno == EWOULDBLOCK) {
            printf("Another instance is already running. Exiting...\n");
            close(*fd);
            return -1;
        } else {
            /* 
             * Handle other locking errors
             * Print error message and clean up file descriptor
             */
            perror("Failed to lock file");
            close(*fd);
            return -1;
        }
    }

    /* 
     * Return success indicating lock was acquired
     * Caller now has exclusive access to the program
     */
    return 0;
}

/* 
 * Release file lock and close file descriptor
 * This function cleans up the locking mechanism
 */
static void unlock_file(int fd) {
    /* 
     * Release the exclusive lock on the file
     * This allows other instances to run
     */
    flock(fd, LOCK_UN);
    
    /* 
     * Close the file descriptor
     * Release system resources associated with the lock file
     */
    close(fd);
}

/* 
 * Signal handler function for process signal management
 * This function handles various signals sent to the process
 */
static void signal_handler(int sig) {
    /* 
     * Switch statement to handle different signal types
     * Each case handles a specific signal with appropriate action
     */
    switch (sig) {
    case SIGINT: /* ctrl + c */
        /* 
         * Handle SIGINT signal (Ctrl+C)
         * Perform cleanup operations and exit gracefully
         */
        close_log();
        close_appprofile_ctl();
        exit(0);
        break;
    case SIGPIPE:
        /* 
         * Handle SIGPIPE signal (broken pipe)
         * No action needed, continue execution
         */
        break;
    default:
        /* 
         * Handle any other unexpected signals
         * No specific action for unhandled signals
         */
        break;
    }
}

/* 
 * Check if another instance of the program is already running
 * This function uses ps command to detect running processes
 */
int is_program_running(const char *program_name) {
    char command[256];
    FILE *fp;
    int count = 0;

    /* 
     * Construct ps command to count running instances
     * Excludes grep and tail processes from the count
     */
    snprintf(command, sizeof(command), "ps aux | grep '%s' | grep -v 'grep' | grep -v 'tail' | wc -l", program_name);
    
    /* 
     * Execute the ps command and open pipe for reading output
     * The pipe captures the command output for processing
     */
    fp = popen(command, "r");
    
    /* 
     * Check if pipe opening was successful
     * Return error if command execution fails
     */
    if (fp == NULL) {
        perror("Failed to run ps command");
        return -1;
    }

    /* 
     * Read the count of running processes from command output
     * The count represents number of program instances
     */
    if (fscanf(fp, "%d", &count) != 1) {
        perror("Failed to read output");
        fclose(fp);
        return -1;
    }

    /* 
     * Close the pipe and release resources
     * Ensure proper cleanup of file pointer
     */
    fclose(fp);
    
    /* 
     * Return whether multiple instances are running
     * Count > 1 indicates another instance is active
     */
    return count > 1;
}

/* 
 * Display help information for the application
 * This function prints usage instructions and command syntax
 */
static void appctl_help(void) {
    /* 
     * Print version and release information
     * Provides context about the application version
     */
    printf("version:V%s,release:%s\n", VERSION, RELEASE_DATE);
    
    /* 
     * Print help command usage
     * Shows how to access help information
     */
    printf("appctl -h, help information.\n");
    
    /* 
     * Print install command with application name and file path options
     * Demonstrates application installation syntax
     */
    printf("appctl -i [-n app-name] [-f file-path], install APP.\n");
    
    /* 
     * Print install command with default package path option
     * Shows alternative installation method
     */
    printf("appctl -i [-p file-path], install APP with package in default path.\n");
    
    /* 
     * Print uninstall command syntax
     * Shows how to remove applications
     */
    printf("appctl -u [-n app-name], uninstall APP.\n");
    
    /* 
     * Print start command syntax
     * Shows how to launch applications
     */
    printf("appctl -s [-n app-name], start APP.\n");
    
    /* 
     * Print stop command syntax
     * Shows how to terminate applications
     */
    printf("appctl -S [-n app-name], stop APP.\n"); 
    
    /* 
     * Print information display command
     * Shows how to retrieve application details
     */
    printf("appctl -I [-n app-name], displays APP information.\n");
    
    /* 
     * Print version display command for appctl
     * Shows how to check application controller version
     */
    printf("appctl -v [-n app-name], displays appctl version.\n");
    
    /* 
     * Print version listing command for installed apps
     * Shows how to check versions of installed applications
     */
    printf("appctl -V [-n app-name], list install app version.\n");
    
    /* 
     * Print status listing command
     * Shows how to check status of applications
     */
    printf("appctl -l [-n app-name], list app status.\n");
}

/**
 * @brief Parse and execute command line options
 * This function serves as the main command line interface handler
 *
 * @param argc Argument count from command line
 * @param argv Argument vector from command line
 * @return Execution result code indicating success or failure
 */
char exec_option_command(int argc, char **argv)
{
    int ret = 0, ch;
    struct app_action appargs;
    
    /* 
     * Initialize application arguments structure
     * Clear memory to ensure clean state for parsing
     */
    memset(&appargs, '\0', sizeof(struct app_action));

    /* 
     * Check if minimum arguments are provided
     * Display help if no arguments given
     */
    if (argc < 2) {
        printf("No arguments provided\n");
        appctl_help();
        return ERROR_ARGUMENT_MISSING;
    }

    /* 
     * Set up signal handlers for graceful interruption
     * Handle SIGPIPE and SIGINT signals appropriately
     */
    signal(SIGPIPE, signal_handler);
    signal(SIGINT, signal_handler);

    /* 
     * Parse command line options using getopt
     * Process each option flag and its arguments
     */
    while ((ch = getopt(argc, argv, "hiusSIVvln:f:p:")) != -1) {
        /* 
         * Switch statement to handle each option character
         * Each case processes a specific command line option
         */
        switch (ch) {
        case 'h':
            /* 
             * Handle help option
             * Display help information and set return code
             */
            appctl_help();
            ret = INFO_ARGE_HELP;
            break;
        case 'i':
            /* 
             * Handle install option
             * Set install flag in arguments structure
             */
            appargs.set_install = 1;
            break;
        case 'u':
            /* 
             * Handle uninstall option
             * Set uninstall flag in arguments structure
             */
            appargs.set_uninstall = 1;
            break;
        case 's':
            /* 
             * Handle start option
             * Set start flag in arguments structure
             */
            appargs.set_start = 1;
            break;
        case 'S':
            /* 
             * Handle stop option
             * Set stop flag in arguments structure
             */
            appargs.set_stop = 1;
            break;
        case 'I':
            /* 
             * Handle get information option
             * Set getinfo flag in arguments structure
             */
            appargs.set_getinfo = 1;
            break;
        case 'n':
            /* 
             * Handle application name option
             * Validate and store application name parameter
             */
            appargs.set_appnane = 1;
            if (strlen(optarg) < CMD_MAX_STRING_LEN) {
                strcpy(appargs.app_name, optarg);
            } else {
                ret = ERROR_STRING_SUPER_LONG;
            }
            break;
        case 'f':
            /* 
             * Handle file path option
             * Validate and store package file path parameter
             */
            appargs.set_filepath = 1;
            if (strlen(optarg) < CMD_MAX_STRING_LEN) {
                strcpy(appargs.package_path, optarg);
            } else {
                ret = ERROR_STRING_SUPER_LONG;
            }
            break;
        case 'p':
            /* 
             * Handle default path option
             * Validate and store default package path parameter
             */
            appargs.set_defaultpath = 1;
            if (strlen(optarg) < CMD_MAX_STRING_LEN) {
                strcpy(appargs.default_package, optarg);
            } else {
                ret = ERROR_STRING_SUPER_LONG;
            }
            break;
        case 'v':
            /* 
             * Handle version display option
             * Print application version information
             */
            printf("version: V%s\n", VERSION);
            ret = INFO_VERSION;
            break;
        case 'V':
            /* 
             * Handle output version option
             * Set output version flag in arguments structure
             */
            appargs.set_out_version = 1;
            break;
        case 'l':
            /* 
             * Handle list option
             * Set list flag in arguments structure
             */
            appargs.set_list = 1;
            break;
        default:
            /* 
             * Handle unknown or invalid options
             * Display help and set error return code
             */
            printf("Command parameter error!\n");
            appctl_help();
            ret = ERROR_ARGE_ERROR;
            break;
        }
    }

    /* 
     * Construct command line string for logging purposes
     * Rebuild the command line from arguments for audit trail
     */
    char command_line[1024] = {0};
    for (int i = 1; i < argc; i++) {
        strcat(command_line, argv[i]);
        if (i < argc - 1) {
            strcat(command_line, " ");
        }
    }

    /* 
     * Initialize logging system
     * Prepare logging infrastructure for operation recording
     */
    init_log();

    /* 
     * Execute application action if no parsing errors occurred
     * Only proceed with execution if command line was valid
     */
    if(!ret){
        ret = appaction_exec(&appargs); 
    }

    /* 
     * Record execution result in log system
     * Create audit trail of command and its outcome
     */
    log_record(ret, command_line);

    /* 
     * Close logging system
     * Ensure proper cleanup of logging resources
     */
    close_log();

    /* 
     * Return execution result code
     * Provide status indication to calling process
     */
    return ret;
}

/* 
 * Record execution results in the log system
 * This function categorizes and logs messages based on result type
 */
void log_record(int ret, char *cmd)
{
    char tmp[EXEC_MES_BUFF_LEN+1] = {0};

    /* 
     * Retrieve message corresponding to result code
     * Get human-readable message for the execution result
     */
    const char* tmp_meg = get_error_message((EXEC_RESULE_CODE)ret);
    
    /* 
     * Copy message to temporary buffer
     * Prepare message for processing and formatting
     */
    strncpy(tmp, tmp_meg, strlen(tmp_meg));
    
    /* 
     * Remove newline characters from message
     * Ensure clean formatting for log output
     */
    for(int i = 0 ; i < strlen(tmp_meg); i++)
    {
        if('\n' == tmp[i])
        {
            tmp[i]='\0';
        }
    }

    /* 
     * Categorize and log message based on message type
     * Route to appropriate log level based on content
     */
    if(strstr(tmp,"info"))
    {
        /* 
         * Log informational messages
         * Record normal operational information
         */
        write_log(MSG_INFO,"appctl %s %s",cmd,tmp);
    }
    else if(strstr(tmp,"error"))
    {
        /* 
         * Log error messages
         * Record error conditions and failures
         */
        write_log(MSG_ERROR,"appctl %s %s",cmd,tmp);
    }
    else if(strstr(tmp,"warning"))
    {
        /* 
         * Log warning messages
         * Record potential issues that don't prevent operation
         */
        write_log(MSG_WARNING,"appctl %s %s",cmd,tmp);
    }
    else if(strstr(tmp,"debug"))
    {
        /* 
         * Log debug messages
         * Record detailed debugging information
         */
        write_log(MSG_DEBUG,"appctl %s %s",cmd,tmp);
    }
    else if(strstr(tmp,"verbose"))
    {
        /* 
         * Log verbose messages
         * Record detailed operational information
         */
        write_log(MSG_INFO,"appctl %s %s",cmd,tmp);
    }
    else
    {
        /* 
         * Log unknown message types as errors
         * Handle unexpected message categories
         */
        write_log(MSG_ERROR,"appctl %s %s",cmd,tmp);
    }
}